package goLogger

import (
	"fmt"
	"os"
	"path"
	"strings"
	"time"
)

//FileLogger日志文件结构体
type FileLogger struct {
	LogLevel       LogLevel
	LogFileName    string
	LogFilePath    string
	FileOBJ        *os.File
	ErrorFileOBJ   *os.File
	LogMaxFileSize int64
}

//FileLogger构造函数返回结构体指针*FileLogger
//levelStr：UNKNOWN、DEBUG、INFO、WARN、ERROR
//filePath：日志文件路径
//maxFileSize：最大文件大小，单位字节(B)
func NewFileLogger(levelStr, fileName, filePath string, maxFileSize int64) (*FileLogger, error) {
	level, err := parseLoglevel(levelStr)
	if err != nil {
		fmt.Printf("日志字符转数字失败")
	}

	f1 := &FileLogger{
		LogLevel:       level,
		LogFileName:    fileName,
		LogFilePath:    filePath,
		LogMaxFileSize: maxFileSize,
	}
	err = f1.initFile()
	if err != nil {
		panic(err)
	}
	return f1, nil

}

//初始化，获取日志文件句柄
func (f *FileLogger) initFile() error {
	fullFilePath := path.Join(f.LogFilePath, f.LogFileName)
	FileOBJ, err := os.OpenFile(fullFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Printf("打开日志文件失败! err:%+v\n", err)
		return err
	}
	f.FileOBJ = FileOBJ

	errFileOBJ, err := os.OpenFile(fullFilePath+".err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Printf("打开错误日志文件失败! err: %+v\n", err)
		return err
	}
	f.ErrorFileOBJ = errFileOBJ

	return nil

}

//检测文件大小
func (f *FileLogger) CheckSize(file *os.File) bool {
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("func CheckSize:在检测文件大小函数中获取文件信息失败！错误提示：%+v\n", err)
		return false
	}
	return fileInfo.Size() >= f.LogMaxFileSize
}

//写日志
func (f *FileLogger) log(l LogLevel, message string, a ...interface{}) {
	if f.enable(l) {
		// fmt.Printf("普通日志：开始文件大小检测：line 88 ：文件内存地址：%+v\n", f.FileOBJ)
		if f.CheckSize(f.FileOBJ) {
			newFileOBJ, err := f.splitLogFile(f.FileOBJ)
			if err != nil {
				fmt.Printf("文件切割失败:%v\n", err)
				return
			}
			f.FileOBJ = newFileOBJ
		}
		msg := fmt.Sprintf(message, a...)
		now := time.Now()
		funcName, fileName, lineNum := GetInfo(3)
		LogLevelString, err := unparseLogLevel(l)
		funcName = strings.Split(funcName, ".")[1]

		if err != nil {
			panic("Unparse LogLevel to string Failed!")
		}
		fmt.Fprintf(f.FileOBJ, "[%s] [%s] [%s:%s:%d] %s\n", now.Format("2006-01-02 15:04:05"), LogLevelString, fileName, funcName, lineNum, msg)
		if l >= ERROR {
			// fmt.Printf("正在判断错误日志等级：文件大小检测：文件内存地址：%+v\n", f.ErrorFileOBJ)
			if f.CheckSize(f.ErrorFileOBJ) {
				newFileOBJ, err := f.splitLogFile(f.ErrorFileOBJ)
				if err != nil {
					fmt.Printf("ERROR级别日志文件创建失败：%+v\n", err)
				}
				f.ErrorFileOBJ = newFileOBJ

			}
			fmt.Fprintf(f.ErrorFileOBJ, "[%s] [%s] [%s:%s:%d] %s\n", now.Format("2006-01-02 15:04:05"), LogLevelString, fileName, funcName, lineNum, msg)
		}
	}

}

//切割日志
func (f *FileLogger) splitLogFile(file *os.File) (*os.File, error) {

	//需要切割的日志文件
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("文件信息获取失败:%+v\n", err)
	}
	nowStr := time.Now().Format("20060102150405000")
	LogName := path.Join(f.LogFilePath, fileInfo.Name())
	bakLogName := fmt.Sprintf("%s%s.bak", LogName, nowStr)

	// 1.关闭当前日志文件
	file.Close()

	// 2.备份旧日志文件xxx20190803.log.bak
	os.Rename(LogName, bakLogName)
	// 3.创建新日志文件
	newFileOBJ, err := os.OpenFile(LogName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		panic(err)
	}
	// f.FileOBJ = newFileOBJ
	// 4. 将新的日志文件句柄负值到f.FileOBJ
	return newFileOBJ, nil
}

//释放文件句柄
func (f *FileLogger) CloseLogFile() error {
	err := f.ErrorFileOBJ.Close()
	if err != nil {
		fmt.Printf("释放文件句柄失败，err: %+v", err)
		return err
	}
	err = f.FileOBJ.Close()
	if err != nil {
		fmt.Printf("释放文件句柄失败，err: %+v", err)
		return err
	}
	return nil
}

//日志开关，传入参数为最低级别
func (f *FileLogger) enable(level LogLevel) bool {
	return level >= f.LogLevel
}

//格式化字符串
func (f *FileLogger) Debug(format string, a ...interface{}) {
	f.log(DEBUG, format, a...)
}

func (f *FileLogger) Info(format string, a ...interface{}) {
	f.log(INFO, format, a...)
}

func (f *FileLogger) Warn(format string, a ...interface{}) {
	f.log(WARN, format, a...)
}

func (f *FileLogger) Error(format string, a ...interface{}) {
	f.log(ERROR, format, a...)
}
